package util.bio.annotation;
import java.io.*;
import java.util.*;

import util.gen.IO;
import util.gen.Num;

/**
 * Simple start end object for sorting. 
 */
public class StartStop implements Comparable, Serializable {
	//fields
	protected int start;
	protected int end;
	
	//constructors
	public StartStop (int start, int end){
		this.start = start;
		this.end = end;
	}
	public StartStop (int[] startEnd){
		this.start = startEnd[0];
		this.end = startEnd[1];
	}
	
	public String toString(){
		return start+"\t"+end;
	}
	/**Assumes coordinates are interbase.*/
	public boolean intersects (int begin, int stop){
		if (stop <= this.start || begin >= this.end) return false;
		return true;
	}
	
	/**Checks to see if each start is <= the stop*/
	public static boolean checkStartStops(StartStop[] ss){
		for (int i=0; i< ss.length; i++){
			if (ss[i].start> ss[i].end) return false;
		}
		return true;
	}
	/**Parses a tab delimited file (chr, start, stop, ...), zip/ gz OK.
	 * @param bed file, skips empty lines and those starting with '#'
	 * @param subStart and subEnd are the number to subtract from the ends of each region
	 * @return a HashMap<Chr,sorted StartStop[]> or null in none are found
	 * */
	public static HashMap<String,StartStop[]> parseStartStops (File bedFile, int subStart, int subEnd){
		HashMap<String,ArrayList<StartStop>> ss = new HashMap<String,ArrayList<StartStop>>();
		try{
			BufferedReader in = IO.fetchBufferedReader(bedFile);
			String line;
			String[] tokens;
			ArrayList<StartStop> al = new ArrayList<StartStop>();
			//chrom, start, stop
			while ((line = in.readLine()) !=null) {
				line = line.trim();
				if (line.length() ==0 || line.startsWith("#")) continue;
				tokens = line.split("\\s+");
				if (tokens.length < 3) continue;
				//does chrom already exist?
				if (ss.containsKey(tokens[0])) al = ss.get(tokens[0]);
				else {
					al = new ArrayList<StartStop>();
					ss.put(tokens[0], al);
				}
				int start = Integer.parseInt(tokens[1]);
				int stop = Integer.parseInt(tokens[2]);
				if (start > stop) throw new Exception("\nFound a start that is greater than stop!  Cannot parse file "+bedFile+", bad line-> "+line);
				al.add(new StartStop(start-subStart, stop- subEnd));
			}
		}catch (Exception e){
			e.printStackTrace();
		}
		if (ss.size() == 0) return null;
		//make hashmap
		HashMap<String,StartStop[]> ssReal = new HashMap<String,StartStop[]>();
		Iterator<String> it = ss.keySet().iterator();
		while (it.hasNext()){
			String chrom = it.next();
			ArrayList<StartStop> al = ss.get(chrom);
			StartStop[] array = new StartStop[al.size()];
			al.toArray(array);
			Arrays.sort(array);
			ssReal.put(chrom, array);
		}
		return ssReal;
	}
	
	/**Returns a StartStop[] for regions defined with baseHitCounts !=0.
	 * Coordinates are interbase.*/
	public static StartStop[] makeStartStops(short[] baseHitCount){
		ArrayList<StartStop> ss = new ArrayList<StartStop>();
		//find first non zero base
		int i=0;
		for (; i< baseHitCount.length; i++) if (baseHitCount[i]!=0) break;
		if (i == baseHitCount.length) return null;
		int start = i;
		int val = baseHitCount[start];
		//find different base
		for (; i< baseHitCount.length; i++){
			if (baseHitCount[i]!=val) {
				//make SS
				if (val!=0) ss.add(new StartStop(start,i));
				start = i;
				val = baseHitCount[start];
			}
		}
		//add last
		if (val!=0) ss.add(new StartStop(start,i));
		StartStop[] ssA = new StartStop[ss.size()];
		ss.toArray(ssA);
		return ssA;
	}
	
	/**Sorts by start base, then by length, smaller to larger for both.*/
	public int compareTo(Object other){
		StartStop se = (StartStop)other;
		if (start<se.start) return -1;
		if (start>se.start) return 1;
		// if same start, sort by length, smaller to larger
		int len = end-start;
		int otherLen = se.end-se.start;
		if (len<otherLen) return -1;
		if (len>otherLen) return 1;
		return 0;
	}
	
	/**Returns the total number of bases, assumes interbase coordinates.*/
	public static int totalBP (StartStop[] ss){
		int total = 0;
		for (int i=0; i< ss.length; i++) total += ss[i].getLength();
		return total;
	}
	
	/**Returns the starts in number of bases, not coordinates for the array.*/
	public static int[] startsInBases (StartStop[] ss){
		int[] indexes = new int[ss.length];
		int index = 0;
		for (int i=0; i< ss.length; i++) {
			index += ss[i].getLength();
			indexes[i] = index;
		}
		return indexes;
	}
	
	/**Assumes interbase coordinates.*/
	public int getLength(){
		return end-start;
	}
	
	public int getMiddle(){
		return Num.calculateMiddleIntergenicCoordinates(start, end);
	}
	
	public int getEnd() {
		return end;
	}
	public int getStart() {
		return start;
	}
	public int[] getStartEnd(){
		return new int[]{start,end};
	}
}
